﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;

using Storm.TextEditor.Core;
using Storm.TextEditor.Drawing.GDI;
using Storm.TextEditor.Win32;

namespace Storm.TextEditor.Editor
{
	/// <summary>
	/// Represents a list of all fonts in the Windows Forms Designer.
	/// </summary>
	public class FontList
		: UITypeEditor
	{
		#region Fields

		private bool handleLostfocus = false;
		private IWindowsFormsEditorService editorService = null;
		private ListBox fontListBox = null;

		#endregion

		#region Methods

		#region Private

		/// <summary>
		/// Called when [draw item].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.DrawItemEventArgs"/> instance containing the event data.</param>
		private void OnDrawItem(object sender, DrawItemEventArgs e)
		{
			bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;

			if (e.Index == -1)
				return;

			object listBoxItem = fontListBox.Items[e.Index];
			string itemText    = listBoxItem.ToString();

			Brush backgroundBrush = null;
			Brush foregroundBrush = null;

			if (selected == true)
			{
				backgroundBrush = SystemBrushes.Highlight;
				foregroundBrush = SystemBrushes.HighlightText;
			}
			else
			{
				backgroundBrush = SystemBrushes.Window;
				foregroundBrush = SystemBrushes.WindowText;
			}

			if (selected == true)
			{
				int xOffset = 37;
				e.Graphics.FillRectangle(SystemBrushes.Window, new Rectangle(xOffset, e.Bounds.Top, e.Bounds.Width - xOffset, fontListBox.ItemHeight));
				e.Graphics.FillRectangle(SystemBrushes.Highlight, new Rectangle(xOffset + 1, e.Bounds.Top + 1, e.Bounds.Width - xOffset - 2,
					fontListBox.ItemHeight - 2));

				ControlPaint.DrawFocusRectangle(e.Graphics, new Rectangle(xOffset, e.Bounds.Top, e.Bounds.Width - xOffset, fontListBox.ItemHeight));
			}
			else
				e.Graphics.FillRectangle(SystemBrushes.Window, 0, e.Bounds.Top, e.Bounds.Width, fontListBox.ItemHeight);


			e.Graphics.DrawString(itemText, e.Font, foregroundBrush, 38, e.Bounds.Top + 4);
			e.Graphics.SetClip(new Rectangle(1, e.Bounds.Top + 2, 34, fontListBox.ItemHeight - 4));
			e.Graphics.FillRectangle(SystemBrushes.Highlight, new Rectangle(1, e.Bounds.Top + 2, 34, fontListBox.ItemHeight - 4));

			IntPtr graphicsHdc = e.Graphics.GetHdc();
			GDIFont itemFont = new GDIFont(itemText, 9);

			IntPtr res = NativeGdi32Api.SelectObject(graphicsHdc, itemFont.hFont);
			NativeGdi32Api.SetTextColor(graphicsHdc, ColorTranslator.ToWin32(SystemColors.Window));

			NativeGdi32Api.SetBkMode(graphicsHdc, 0);

			int tabStopPositions = 0;
			NativeUser32Api.TabbedTextOut(graphicsHdc, 3, e.Bounds.Top + 5, "abc", 3, 0, ref tabStopPositions, 0);

			NativeGdi32Api.SelectObject(graphicsHdc, res);

			itemFont.Dispose();
			e.Graphics.ReleaseHdc(graphicsHdc);
			e.Graphics.DrawRectangle(Pens.Black, new Rectangle(1, e.Bounds.Top + 2, 34, fontListBox.ItemHeight - 4));

			e.Graphics.ResetClip();
		}

		/// <summary>
		/// Called when [mouse down].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
		private void OnMouseDown(object sender, MouseEventArgs e)
		{
			if (handleLostfocus == false && fontListBox.ClientRectangle.Contains(fontListBox.PointToClient(new Point(e.X, e.Y))) == true)
			{
				fontListBox.LostFocus += new EventHandler(OnValueChanged);
				handleLostfocus = true;
			}
		}

		/// <summary>
		/// Called when [value changed].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void OnValueChanged(object sender, EventArgs e)
		{
			if (editorService != null)
				editorService.CloseDropDown();
		}

		#endregion

		#region Public

		/// <summary>
		/// Edits the specified object's value using the editor style indicated by the <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle"/> method.
		/// </summary>
		/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
		/// <param name="provider">An <see cref="T:System.IServiceProvider"/> that this editor can use to obtain services.</param>
		/// <param name="value">The object to edit.</param>
		/// <returns>
		/// The new value of the object. If the value of the object has not changed, this should return the same object it was passed.
		/// </returns>
		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
			if (context != null && context.Instance != null && provider != null)
			{
				editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
				if (editorService != null)
				{
					fontListBox = new ListBox();

					fontListBox.DrawMode    = DrawMode.OwnerDrawFixed;
					fontListBox.BorderStyle = BorderStyle.None;
					fontListBox.Sorted      = true;

					fontListBox.ItemHeight = 20;
					fontListBox.Height     = 200;
					fontListBox.Width      = 180;

					fontListBox.MouseDown   += OnMouseDown;
					fontListBox.DoubleClick += OnValueChanged;
					fontListBox.DrawItem    += OnDrawItem;

					ICollection fonts = new FontEnum().EnumFonts();
					foreach (string font in fonts)
						fontListBox.Items.Add(font);

					editorService.DropDownControl(fontListBox);
					if (fontListBox.SelectedItem != null)
						return fontListBox.SelectedItem.ToString();
				}
			}

			return value;
		}

		/// <summary>
		/// Gets the editor style used by the <see cref="M:System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object)"/> method.
		/// </summary>
		/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
		/// <returns>
		/// A <see cref="T:System.Drawing.Design.UITypeEditorEditStyle"/> value that indicates the style of editor used by the <see cref="M:System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object)"/> method. If the <see cref="T:System.Drawing.Design.UITypeEditor"/> does not support this method, then <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle"/> will return <see cref="F:System.Drawing.Design.UITypeEditorEditStyle.None"/>.
		/// </returns>
		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
		{
			return UITypeEditorEditStyle.DropDown;
		}

		/// <summary>
		/// Paints a representation of the value of an object using the specified <see cref="T:System.Drawing.Design.PaintValueEventArgs"/>.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Drawing.Design.PaintValueEventArgs"/> that indicates what to paint and where to paint it.</param>
		public override void PaintValue(PaintValueEventArgs e)
		{
			string text = e.Value.ToString();

			Bitmap bitmap = new Bitmap(e.Bounds.Width, e.Bounds.Height);
			Graphics graphics = Graphics.FromImage(bitmap);

			Brush backgroundBrush = SystemBrushes.Window;
			Brush foregroundBrush = SystemBrushes.WindowText;

			graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);

			IntPtr graphicsHdc = graphics.GetHdc();
			GDIFont itemFont = new GDIFont(text, 9);

			IntPtr selectObject = NativeGdi32Api.SelectObject(graphicsHdc, itemFont.hFont);
			NativeGdi32Api.SetTextColor(graphicsHdc, ColorTranslator.ToWin32(SystemColors.Window));

			NativeGdi32Api.SetBkMode(graphicsHdc, 0);

			int tabStopPositions = 0;
			NativeUser32Api.TabbedTextOut(graphicsHdc, 1, 1, "abc", 3, 0, ref tabStopPositions, 0);
			NativeGdi32Api.SelectObject(graphicsHdc, selectObject);

			itemFont.Dispose();
			graphics.ReleaseHdc(graphicsHdc);
			e.Graphics.DrawImage(bitmap, e.Bounds.Left, e.Bounds.Top);
		}

		/// <summary>
		/// Indicates whether the specified context supports painting a representation of an object's value within the specified context.
		/// </summary>
		/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
		/// <returns>
		/// true if <see cref="M:System.Drawing.Design.UITypeEditor.PaintValue(System.Object,System.Drawing.Graphics,System.Drawing.Rectangle)"/> is implemented; otherwise, false.
		/// </returns>
		public override bool GetPaintValueSupported(ITypeDescriptorContext context)
		{
			return true;
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of the <see cref="FontList"/> class.
		/// </summary>
		public FontList()
		{
		}
	}
}
